package com.example.concurrency.queue;

import lombok.Getter;
import lombok.ToString;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *
 * </p>
 *
 * @author Ashe
 * @version V1.2.0
 * @date 2019年08月20日 17:09
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2019年08月20日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class DelayQueueEx {

    /**
     * 测试Delay属性
     *
     * @param args
     */
    public static void main(String[] args) {
        DelayQueue<DelayElement> queue = new DelayQueue<>();
        for (int i = 0; i < 10; i++) {
            long delay = Double.valueOf(Math.random() * 10000).longValue();
            queue.put(new DelayElement(delay, "task:" + i));
        }
        DelayElement ele = null;
        while (true) {
            try {
                if ((ele = queue.take()) != null) {
                    System.out.println(ele.toString());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 延迟元素
     */
    @Getter
    @ToString
    static class DelayElement implements Delayed {

        /**
         * 延迟时间
         */
        long delay;
        /**
         * 到期时间
         */
        long expire;
        /**
         * 任务名称
         */
        String name;

        public DelayElement(long delay, String name) {
            this.delay = delay;
            this.name = name;
            this.expire = System.currentTimeMillis() + delay;
        }

        /**
         * Returns the remaining delay associated with this object, in the
         * given time unit.
         * 剩余时间 = 到期时间 - 当前时间
         *
         * @param unit the time unit
         * @return the remaining delay; zero or negative values indicate
         * that the delay has already elapsed
         */
        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        /**
         * Compares this object with the specified object for order.  Returns a
         * negative integer, zero, or a positive integer as this object is less
         * than, equal to, or greater than the specified object.
         *
         * <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
         * -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
         * implies that <tt>x.compareTo(y)</tt> must throw an exception iff
         * <tt>y.compareTo(x)</tt> throws an exception.)
         *
         * <p>The implementor must also ensure that the relation is transitive:
         * <tt>(x.compareTo(y)&gt;0 &amp;&amp; y.compareTo(z)&gt;0)</tt> implies
         * <tt>x.compareTo(z)&gt;0</tt>.
         *
         * <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
         * implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
         * all <tt>z</tt>.
         *
         * <p>It is strongly recommended, but <i>not</i> strictly required that
         * <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>.  Generally speaking, any
         * class that implements the <tt>Comparable</tt> interface and violates
         * this condition should clearly indicate this fact.  The recommended
         * language is "Note: this class has a natural ordering that is
         * inconsistent with equals."
         *
         * <p>In the foregoing description, the notation
         * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
         * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
         * <tt>0</tt>, or <tt>1</tt> according to whether the value of
         * <i>expression</i> is negative, zero or positive.
         *
         * @param o the object to be compared.
         * @return a negative integer, zero, or a positive integer as this object
         * is less than, equal to, or greater than the specified object.
         * @throws NullPointerException if the specified object is null
         * @throws ClassCastException   if the specified object's type prevents it
         *                              from being compared to this object.
         */
        @Override
        public int compareTo(Delayed o) {
            return Long.valueOf(this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS)).intValue();
        }
    }
}

